package com.oner365.test.volatileDemo;

public class SingletonDemo {
  
  //多线程情况下使用volatile可以保证DCL100%准确率
  //在读写前会禁止指令重排，在前后加内存屏障（Memory Barrier） 保证变量内存可见性，强制刷出cpu内的缓存
  volatile static SingletonDemo instance = null;

  private SingletonDemo() {
      System.out.println(Thread.currentThread().getName() + "\t 执行构造方法SingletonDemo()");
  }

  public static SingletonDemo getInstance() {
        // 单例模式再多线程情况下会导致构造方法多次被执行，执行单例指令步骤如下
        // １）分配对象内存空间：memory = allocate();
        // ２）初始化对象：instance(memory);
        // ３）设置instance指向分配的内存地址：instance = memory;
//      if (instance == null) {
//          instance = new SingletonDemo();
//      }
      //多线程创建依然是上面的三个步骤，但由于第2和3步之间没有数据依赖性，执行顺序可能被cpu或编译器重排
      // 出现132执行顺序，也就是对象并未执行完初始化，这时如果线程2抢到cpu并开始执行可能出现instance != null，会返回实例，导致程序空指针异常
      // DCL这种错误概率极低但确实会出现，所以用volatile可以保证初始化时指令执行顺序禁止cpu或编译器指令重排
      // volatile禁止重排的方式就是会在执行时在前后加内存屏障（Memory Barrier）
      // DCL(Double Check Lock双端检锁机制)
      if (instance == null) {  
          synchronized (SingletonDemo.class) {
              if (instance == null) {  
                  instance = new SingletonDemo(); 
              }
          }
      }
      return instance;
  }
  
  public static void main(String[] args) {
      // 单线程测试
//    System.out.println("单线程的情况测试开始");
//    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//    System.out.println("单线程的情况测试结束\n");

    // 多线程测试
    System.out.println("多线程的情况测试开始");
    for (int i=1; i<=10; i++) {
        new Thread(() -> {
            SingletonDemo.getInstance();
        }, String.valueOf(i)).start();
    }
}

}
